/*
 * Title:        CloudScheduler Toolkit
 * Description:  a Toolkit for Modeling and Simulation of Job Scheduling and Resource Provisioning in Cloud System
 * Licence:      GPL - http://www.gnu.org/copyleft/gpl.html
 * Author:       Fuhui Wu
 *
 * Copyright (c) 2013-2014, The National University of Defense Technology, China
 */

package org.nudt.jCloud.workload;

import java.util.ArrayList;
import java.util.List;

import org.nudt.jCloud.infrastructure.SlotType;
import org.nudt.jCloud.simulation.Batch;
import org.nudt.jCloud.simulation.CloudSchedSIM;
import org.nudt.jCloud.simulation.SIMTag;
import org.nudt.jCloud.util.Log;

public abstract class Job implements Comparable<Job>{
	private int id;
	private double arrivalTime;
	private List<Task> taskList;
	private List<Task> unScheduledTasks;
	private List<Task> scheduledTasks;
	private List<Task> finishedTasks;
	private boolean finished;
	private double finishTime;
	private LogicalJob xJob;	
	private SlotType slotType;
	
	private int index = 0;
	
	public void reset() {
		setIndex(0);
		setFinished(false);
		setFinishTime(-1);
		unScheduledTasks.removeAll(unScheduledTasks);
		unScheduledTasks.addAll(taskList);
		scheduledTasks.removeAll(scheduledTasks);
		finishedTasks.removeAll(finishedTasks);
		
	}

	public Job(double arrivalTime, List<Task> taskList, SlotType slotType, LogicalJob xJob){
		setId(0);
		setArrivalTime(arrivalTime);
		setFinishTime(-1);
		setTaskList(taskList);
		setUnScheduledTasks(new ArrayList<Task>());
		setScheduledTasks(new ArrayList<Task>());
		setFinishedTasks(new ArrayList<Task>());
		for(int i=0; i<this.size(); ++i){
			taskList.get(i).setJob(this);
			getUnScheduledTasks().add(taskList.get(i));
		}
		setFinished(false);
		setSlotType(slotType);
		setXJob(xJob);
	}

	/**
	 * When a batch finish, this function is used to update the job's state.
	 */
	public void finishBatch(Batch batch) {
		for(int i=0; i<batch.size(); ++i){
			getScheduledTasks().remove(batch.getTaskList().get(i));
			getFinishedTasks().add(batch.getTaskList().get(i));
			Log.taskLog(getXJob().getWl().getUser().getUserName(), CloudSchedSIM.getClock(), getXJob().getId(), 
					getId(), batch.getTaskList().get(i).getId(), SIMTag.TASK_FINISH, 
					batch.getTaskList().get(i).getSlot().getHost().getId(), batch.getTaskList().get(i).locality());
			batch.getTaskList().get(i).setSlot(null);
		}
		
		/**
		 * log batch finish.
		 */
		Log.println(this.getXJob().getWl().getUser().getUserBroker().getName()+"    finish "+this.getXJob().type()
				+"-"+this.getXJob().getId()+"    Job: "+this.getId()+"    Batch: "+batch.getId());
		Log.jobLog(getXJob().getWl().getUser().getUserName(), CloudSchedSIM.getClock(), getXJob().getId(), SIMTag.XJOB_UPDATE);
		
		if(getFinishedTasks().size()>=size()){
			Log.println(this.getXJob().getWl().getUser().getUserBroker().getName()+"    finish "+this.getXJob().type()+"    "+this.getXJob().getId()+"    job: "+this.getId());
			setFinished(true);
			this.getXJob().update();
		}		
	}
	
	/**
	 * When a batch is scheduled, this function is used to update the job's state.
	 * Only scheduled batch will get an id, the created batch may be just for resource request, so, 
	 * it may be not scheduled.
	 */
	public void scheduleBatch(Batch batch){
		batch.setId(index++);
		for(int i=0; i<batch.size(); ++i){
			Log.taskLog(getXJob().getWl().getUser().getUserName(), CloudSchedSIM.getClock(), getXJob().getId(), 
					getId(), batch.getTaskList().get(i).getId(), SIMTag.TASK_SCHEDULE, 
					batch.getTaskList().get(i).getSlot().getHost().getId(), batch.getTaskList().get(i).locality());
		}
		
		Log.println(this.getXJob().getWl().getUser().getUserBroker().getName()+"    schedule "+getXJob().type()+"-"
				+getXJob().getId()+"    Job: "+getId()+"    finish at: "+batch.getFinishTime());
		Log.jobLog(getXJob().getWl().getUser().getUserName(), CloudSchedSIM.getClock(), getXJob().getId(), SIMTag.XJOB_SCHEDULE);
	}
		
	public abstract Batch nextBatch(int maxSize);
	public abstract String getType();
	
	public int size(){
		return taskList.size();
	}
	
	public Batch createBatch(){
		Batch batch = new Batch(this);
		return batch;
	}
		
	/**
	 * @return the id
	 */
	public int getId() {
		return id;
	}

	/**
	 * @param id the id to set
	 */
	public void setId(int id) {
		this.id = id;
	}

	/**
	 * @return the arrivalTime
	 */
	public double getArrivalTime() {
		return arrivalTime;
	}

	/**
	 * @param arrivalTime the arrivalTime to set
	 */
	public void setArrivalTime(double arrivalTime) {
		this.arrivalTime = arrivalTime;
	}

	/**
	 * @return the finishTime
	 */
	public double getFinishTime() {
		return finishTime;
	}

	/**
	 * @param finishTime the finishTime to set
	 */
	public void setFinishTime(double finishTime) {
		this.finishTime = finishTime;
	}

	/**
	 * @return the taskList
	 */
	public List<Task> getTaskList() {
		return taskList;
	}

	/**
	 * @param taskList the taskList to set
	 */
	public void setTaskList(List<Task> taskList) {
		this.taskList = taskList;
	}

	/**
	 * @return the finished
	 */
	public boolean isFinished() {
		return finished;
	}

	/**
	 * @param finished the finished to set
	 */
	public void setFinished(boolean finished) {
		this.finished = finished;
	}

	@Override
	public int compareTo(Job job) {
		if(job == null){
			return 1;
		}else if(arrivalTime < job.getArrivalTime()){
			return -1;
		}else if(arrivalTime > job.getArrivalTime()){
			return 1;
		}else if(this == job){
			return 0;
		}else{
			return 1;
		}
	}

	public SlotType getSlotType() {
		return slotType;
	}

	public void setSlotType(SlotType slotType) {
		this.slotType = slotType;
	}

	public LogicalJob getXJob() {
		return xJob;
	}

	public void setXJob(LogicalJob xJob) {
		this.xJob = xJob;
	}

	public int getIndex() {
		return index;
	}

	public void setIndex(int index) {
		this.index = index;
	}

	public List<Task> getFinishedTasks() {
		return finishedTasks;
	}

	public void setFinishedTasks(List<Task> finishedTasks) {
		this.finishedTasks = finishedTasks;
	}

	public List<Task> getUnScheduledTasks() {
		return unScheduledTasks;
	}

	public void setUnScheduledTasks(List<Task> unScheduledTasks) {
		this.unScheduledTasks = unScheduledTasks;
	}

	public List<Task> getScheduledTasks() {
		return scheduledTasks;
	}

	public void setScheduledTasks(List<Task> scheduledTasks) {
		this.scheduledTasks = scheduledTasks;
	}

}
